"use strict";

let request = require('request');
var debugLog = require('../../function.js').debugLog;
var Pool = require('./Pool.js');

function BaseEsDao() {
}
module.exports = BaseEsDao;

let theProto = BaseEsDao.prototype;

theProto.init = function (metaInfo, esClient) {
  this.metaInfo = metaInfo;
  this.index = esClient.index;
  this.type = metaInfo.type;
  this.esClient = esClient;
  this.host = esClient.host;
  this.client = esClient.client;
  this.typeReady = false;
  this.idFieldName = metaInfo.idFieldName;
  esClient.createMappingIfNecessary(this.type, metaInfo.mapping);

  this.pool = new Pool();
};

theProto.checkReady = function () {
  if (!this.typeReady) {
    this.typeReady = this.esClient.readyType.indexOf(this.type) >= 0;
  }
  if (!this.typeReady) {
    console.error('elastic not reachable');
    //throw new Error('elastic search not ready');
  }
};

/**
 * 这里有个bug，如果q的文本很长，可以会出错。【估计内部使用了get实现，然而get的url长度有限】
 * quick search
 * {q: q, size: size, from: from}
 * @param json
 * @returns {*}
 */
theProto.qSearch = function*(json) {
  debugLog('qSearch', json);
  if (json.q.length > 400) {
    return yield this.qSearchLong(json);
  }

  let self = this;
  let res = yield this.client.search({
    index: self.index,
    type: self.type,
    q: json.q,
    from: json.from,
    size: json.size
  });

  return res.hits;
};

/**
 * 删除数据,和 mapping
 */
theProto.deleteMapping = function*() {
  let self = this;
  self.typeReady = false;
  self.esClient.dropTypeReady(self.type);
  // delete the mapping
  let result = yield self.client.indices.deleteMapping({
    index: self.index,
    type: self.type
  });
  return result;
};

/**
 * 重建mapping
 * @returns {*}
 */
theProto.createMapping = function*() {
  let self = this;
  let result = yield self.client.indices.putMapping({
    index: self.index,
    type: self.type,
    ignoreConflicts: true,
    body: self.metaInfo.mapping
  });
  return result;
};

/**
 * 删除 数据 & mapping,重建mapping
 * @returns {*}
 */
theProto.reBuildType = function*() {
  yield this.deleteMapping();
  let res = yield this.createMapping();
  return res;
};

theProto.deleteById = function *(id) {
  let self = this;
  let params = {
    index: self.index,
    type: self.type,
    id: id
  };
  let res = yield self.client.delete(params);
  return res;
};

/**
 * 发送请求
 * @return Promise
 */
theProto.requestSend = function*(json) {
  return new Promise(function (fullfil, reject) {
    request(json, function (err, res, body) {
      if (err) console.error('es not reachable');
      fullfil(body);
    });
  }).then(function (body) {
      return body;
    });
};


/**
 * 发送 get请求
 * @param url
 * @returns string
 */
theProto.requestGet = function*(url) {
  let result = yield this.requestSend({url: url});
  return result;
};

theProto.requestDelete = function*(url) {
  var json = {
    method: 'DELETE',
    url: url
  };
  let result = yield this.requestSend(json);
  return result;
};

/**
 * 发送 post 请求
 * @param url
 * @returns {*}
 */
theProto.requestPost = function*(url, body) {
  var json = {
    method: 'POST',
    url: url,
    json: true,
    headers: {"content-type": "application/json"},
    body: body
  };
  let result = yield this.requestSend(json);
  return result;
};

/**
 * 全部删除:
 * @returns {Promise}
 */
let urlPatternDelete = 'http://host/index/type';
theProto.deleteAll = function*() {
  let url = urlPatternDelete.replace('host', this.host);
  url = url.replace('index', this.index);
  url = url.replace('type', this.type);
  let result = yield this.requestDelete(url);
  return result;
};

/**
 * 获取index mapping
 */
theProto.getIndex = function*() {
  let self = this;
  let result = yield this.client.indices.getMapping({
    index: self.index,
    type: self.type
  });
  return result[self.index].mappings;
};
/**
 * 添加一个文档
 * @param json
 * @returns {*}
 */
theProto.createSingle = function*(json) {
  console.log(' [debug] base: createSingle' + JSON.stringify(json));
  this.checkReady();
  let self = this;
  let item = {
    index: self.index,
    type: self.type,
    body: json
  };
  if (json[self.idFieldName]) {
    item.id = json[self.idFieldName];
  }
  let result = yield this.client.create(item);
  return result;
};

theProto.getById = function*(tid) {
  var self = this;
  try{
    let result = yield this.client.get({
      index: self.index,
      type: self.type,
      id: tid
    });
    return result.found ? result._source : null;
  }catch(err){
    return null;
  }
};

/**
 * bulk接口实现批量创建
 * @param list
 * @param idFieldName
 */
theProto.createMany = function*(list) {
  let body = [];
  let self = this;
  // 组装bulk对象
  for (let i = 0; i < list.length; i++) {
    let json = list[i];
    let index = {
      _index: self.index,
      _type: self.type
    };
    if (json[self.idFieldName]) {
      index._id = json[self.idFieldName];
    }

    let action = {index: index};
    body.push(action, json);
  }

  console.log('body', body);
  let result = yield self.client.bulk({body: body});
  console.log('result');
  console.log(JSON.stringify(result));
  return result;
};

/**
 * 词频查询
 * @param field
 * @param topNum
 */
theProto.termFreq = function*(field, topNum) {
  let self = this;
  self.checkReady();

  let url = this.getTermFreqUrl(field);
  let result = yield this.requestGet(url);
  result = JSON.parse(result);
  let ret = result.terms;

  // 后续处理,包括排序,和简单的筛选,sort by totalfreq desc
  // filter
  let retFilter = [];
  ret.forEach(function (item) {
    if (self.filter(item.term)) retFilter.push(item);
  });

  retFilter.sort(function (a, b) {
    return b.totalfreq - a.totalfreq;
  });

  retFilter.splice(topNum);
  //let needKey = ["totalfreq", "term"];
  let ret2 = {};
  retFilter.forEach(function (item) {
    ret2[item.term] = item.totalfreq;
  });

  return ret2;
};

/**
 * 对一个指定id的帖子，进行词频查询
 * @param id
 * @param fieldName
 * @param isDebug
 */
theProto.termVector = function* (id, fieldName, isDebug) {
  var self = this;
  let url = this.getTermVectorUrl(id);
  let result = yield  this.requestGet(url);
  result = JSON.parse(result);
  if (!result.found) {
    return null;
  }
  if (!result.term_vectors[fieldName]) {
    return null;
  }

  var terms = result.term_vectors[fieldName].terms;
  //terms 的一个对象的结构  terms = {obj1, obj2 ,...}
  // obj1 =>
  //    "你好":{
  //        "term_freq": 4,
  //        "tokens": [{ "position": 294, "start_offset": 378, "end_offset": 379 }]
  //    }

  // delete unused field
  let filterA = {};
  let delWord = []; //debug
  for (let key of Object.keys(terms)) {
    if (self.filter(key)) {
      filterA[key] = terms[key].term_freq;
    } else if (isDebug) {
      delWord.push(key);
    }
  }
  return !!isDebug ? {
    'keep': filterA,
    'delete': delWord
  } : filterA;
};
let urlPatternTermVector = 'http://host/index/type/id/_termvector';
theProto.getTermVectorUrl = function (id) {
  let url = urlPatternTermVector.replace('host', this.host);
  url = url.replace('index', this.index);
  url = url.replace('type', this.type);
  url = url.replace('id', id);
  return url;
};


theProto.cleanReq = function* () {
  yield this.pool.postRequestHandle();
  return 'ok';
};

/**
 * 因为termFrequency 不是标准api,所以只能自己组装url,发送请求等待结果
 * /o2o/_termlist?field=title
 * @param field
 * @returns {string}
 */
let urlPatternTermFreq = 'http://host/index/_termlist';
theProto.getTermFreqUrl = function (field) {
  let url = urlPatternTermFreq.replace('host', this.host);
  url = url.replace('index', this.index);
  url = !!field ? url + '?field=' + field : url;
  return url;
};

/**
 * 词语过滤v2版 , 过滤一些相对"无意义"的词汇
 * @param word
 * @returns {boolean}
 */
theProto.filter = function (word) {
  return filterChain.doFilte(word);
};

/**
 * 词汇过滤结果
 * @param wordList {array}
 * @returns {array} 过滤后的词汇
 */
theProto.wordFilter = function (wordList) {
  return wordList.filter(function (word) {
    return filterChain.doFilte(word);
  });
};

var filterChain = require('./filter/ChainFilter.js').defaultFilterChain(true);

let FLAG_USE_POOL = true;
// 这些方法是与 es-服务器进行请求的方法
let connMethodNames = [
  'qSearch',
  'cScore',
  'deleteMapping',
  'createMapping',
  'reBuildType',
  'deleteById',
  'deleteAll',
  'createSingle',
  'createMapping',
  'termFreq'];
if (FLAG_USE_POOL) {
  for (let i = 0; i < connMethodNames.length; i++) {
    var fnName = connMethodNames[i];
    var originFun = theProto[fnName];

    theProto[fnName] = (function (fn) {
      return function*() {
        var self = this;
        let checkResult = yield this.pool.preRequestHandle();
        if (checkResult.error) {
          console.error(checkResult.error);
          return checkResult;
        }
        //----------
        var result = yield fn.apply(self, arguments);
        //-----------
        yield self.pool.postRequestHandle();
        return result;
      }
    })(originFun);
  }
}
